<script setup lang="ts">
import { onLoad, onUnload } from '@dcloudio/uni-app'
import dayjs from 'dayjs'
import { chooseImage, navigateTo, uploadFile } from '@uni-helper/uni-promises'
import Calendar from '@/components/Calendar.vue'
import { getSignRecord, postPunchIn } from '@/package-clock/api/sign'
import type { SignRecordDto } from '@/package-clock/api/sign/type'
import { amapRegeo, toast } from '@/utils/utils'
import globalVariable from '@/utils/globalVariable'
import { querySalaryCard } from '@/utils/salary-card'

const projectId = ref()
const type = ref() // 1 长期工 0 日结工

const initDate = ref()
const todayDate = ref()
const currentTime = ref('')
const detail = ref<SignRecordDto>({} as SignRecordDto)

const toWorkStatus = ref(1) // 上班状态 1 正常 0迟到 2不显示
const offWorkStatus = ref(1) // 下班状态 1 正常 0早退 2不显示
const commutingStatus = ref(1) // 只有上班和下班都是 正常 那么他才是真正常
const changeDate = ref('') // 选择上面日期发生改变的日期
const macAddressList = ref([]) // 打卡设备列表

const clockAllowHour = 3 // 允许提前打卡范围 默认提前3小时

const isNightOk = ref(false) // 是否允许夜班打卡
const nightTime = ref('') // 夜班时间

const timer = ref(0)

const latitude = ref()
const longitude = ref()
const formatAddress = ref('')
const circularColor = ref(false) // false是不在范围 true在范围,
const abnormal = ref(false) // true显示蓝色正常 false显示黄色早退
const describeRange = ref('正在定位，打卡位置:')
const signInName = ref('定位中')

const devices = ref([])

const toBeWithdraw = ref(false) // 是否需要提现

const staticData = {
    authenticateNumber: '', // 用于判断是否跳转开通薪工卡
    signVoucher: '', // 拍照打卡图片URL
    isOpenClock: false, // 是否全天打卡
}

/**
 * 设置当前时间
 *
 * @returns 无返回值
 */
function setCurrentTime() {
    currentTime.value = dayjs().format('HH:mm:ss')
}

/**
 * 获取授权位置信息
 *
 * @returns 返回一个Promise，resolve返回授权结果，reject返回错误信息
 */
async function getAuth() {
    // const res = await uni.authorize({
    //     scope: 'scope.userLocation',
    // })
    // #ifdef MP-ALIPAY
    const res = await my.getLocation({
        type: 1,
    }).catch(async (err) => {
        console.log('error', err)
        await uni.showModal({
            content: '未授权获取位置信息权限无法打卡，请授权后再试',
        })
        const res = await my.openSetting({})
        if (res.authSetting.location) {
            const locationRes = await my.getLocation({
                type: 1,
            })
            longitude.value = locationRes?.longitude
            latitude.value = locationRes?.latitude
        }
    })
    if (res) {
        longitude.value = res?.longitude
        latitude.value = res?.latitude
    }
    initShow()
    // #endif
}

/**
 * 初始化显示
 *
 * @param time 时间，默认为当前日期，格式为'YYYY-MM-DD'
 * @returns 无返回值
 */
async function initShow(time?: string) {
    !time && (time = dayjs().format('YYYY-MM-DD'))
    const res = await getSignRecord({
        projectId: projectId.value,
        nowDate: time,
    })
    detail.value = res
    changeDate.value = time
    staticData.isOpenClock = res.signTimeStart === '00:00' && res.signTimeEnd === '00:00'
    // 老数据处理回显
    initClockData(res)
    // 处理打卡按钮逻辑
    initButton(res)
    // 处理提示逻辑
    initTips(res)
}

/**
 * 初始化打卡数据老数据回显
 *
 * @param res 打卡记录数据
 */
function initClockData(res: SignRecordDto) {
    const timeStart = `${changeDate.value} ${res?.signTimeStart}`
    const timeEnd = `${changeDate.value} ${res?.signTimeEnd}`
    const needSignTimeStart = dayjs(timeStart).valueOf()
    let needSignTimeEnd = dayjs(timeEnd).valueOf()
    // 全天打卡
    const isOpenClock = staticData.isOpenClock
    // 员工实际打卡时间
    const signTimeInStamp = res.signTimeIn
    const signTimeOutStamp = res.signTimeOut
    const signTimeIn = dayjs(signTimeInStamp || 0).valueOf()
    const signTimeOut = dayjs(signTimeOutStamp || 0).valueOf()
    // 判断是否为夜班打卡
    const isNight = needSignTimeStart > needSignTimeEnd

    if (isNight) {
        // 是夜班打卡的话isNight为当天的前一天
        nightTime.value = dayjs(needSignTimeEnd).subtract(1, 'day').format('YYYY-MM-DD')
        // 如果是夜班，给下班卡增加24小时
        needSignTimeEnd += 24 * 60 * 60 * 1000
        // 当前时间小于下班时间3小时以内，允许在前一天打卡
        isNightOk.value = dayjs().valueOf() < dayjs(needSignTimeEnd).add(3, 'hour').valueOf()
        // 如果是夜班，当前时间没有到第二天的上班时间，就将页面转到前一天
        if (dayjs().valueOf() < (needSignTimeStart - 4 * 60 * 60 * 1000) && !initDate.value) {
            initDate.value = nightTime.value
            return
        }
    }

    // 上班打卡显示处理
    if (signTimeInStamp === null)
        toWorkStatus.value = 2
    else if (needSignTimeStart >= signTimeIn || isOpenClock)
        toWorkStatus.value = 1
    else
        toWorkStatus.value = 0

    // 下班打卡显示处理
    if (signTimeOutStamp === null)
        offWorkStatus.value = 2
    else if (needSignTimeEnd <= signTimeOut || isOpenClock)
        offWorkStatus.value = 1
    else
        offWorkStatus.value = 0

    // 判断上下班打卡是否正常
    if (isOpenClock || (toWorkStatus.value === 1 && offWorkStatus.value === 2 && detail.value.date === todayDate.value)) {
        // 上班打卡正常 下班卡未打 且打卡日期为当天日期 显示为正常
        commutingStatus.value = 1
    }
    else if (
        (toWorkStatus.value === 1 && offWorkStatus.value === 2 && detail.value.date !== todayDate.value)
        || (toWorkStatus.value !== 1 || offWorkStatus.value !== 1)
    ) {
        // 上班打卡异常下班卡未打但打卡日期不为当天日期显示为异常  上班卡或者下班卡未打的都显示为异常
        commutingStatus.value = 0
    }
    if (toWorkStatus.value === 2 && offWorkStatus.value === 2)
        commutingStatus.value = 2
}

/**
 * 初始化打开按钮
 *
 * @param res 签名记录对象
 */
function initButton(res: SignRecordDto) {
    getPositionInfomation(longitude.value || 0, latitude.value || 0, res)
}

/**
 * 获取位置信息并正确显示打卡按钮的文案
 *
 * @param lon 经度
 * @param lat 纬度
 * @param res 打卡记录对象
 * @returns 无返回值
 */
async function getPositionInfomation(lon: number, lat: number, res: SignRecordDto) {
    // 获取需要打卡的坐标（获取整数防止浮点数运算）
    const companyCoordinateX = Number(detail.value.signLocation.split(',')[0]) * 1000000
    const companyCoordinateY = Number(detail.value.signLocation.split(',')[1]) * 1000000
    // 获取当前坐标
    const longitudeX = Number(lon) * 1000000
    const latitudeY = Number(lat) * 1000000
    // 设置打卡范围
    const signRange = (detail.value.signRange || 0) * 10 + 500
    // 当前时间戳
    const timeStamp = dayjs().valueOf()
    // 员工需要打卡的时间
    const timeStart = `${changeDate.value} ${res.signTimeStart}`
    const timeEnd = `${changeDate.value} ${res.signTimeEnd}`
    const needSignTimeStart = dayjs(timeStart).valueOf()
    let needSignTimeEnd = dayjs(timeEnd).valueOf()
    needSignTimeEnd += needSignTimeStart > needSignTimeEnd ? 24 * 60 * 60 * 1000 : 0
    // 全天打卡
    const isOpenClock = staticData.isOpenClock
    // 拍照打卡  0 拍照打卡  1 人脸打卡
    const isPhoto = detail.value.signInMode === 0
    const photoText = isPhoto ? '拍照' : ''
    // 确定打卡类型， 上班  下班
    const isToWork = !detail.value.signTimeIn
    // 迟到
    const isLate = !isOpenClock && isToWork && needSignTimeStart < timeStamp
    // 早退
    const isEarly = !isOpenClock && !isToWork && needSignTimeEnd > timeStamp
    // 是否处于打卡时间段
    const isNotClockTime = !isOpenClock && timeStamp < (needSignTimeStart - clockAllowHour * 60 * 60 * 1000)
    // 是否超出打卡时间段
    const isOutClockTime = !isOpenClock && timeStamp > (needSignTimeEnd + clockAllowHour * 60 * 60 * 1000)
    // 是否处于打卡方位
    const isInLocation = isPhoto || (longitudeX > companyCoordinateX - signRange && longitudeX < companyCoordinateX + signRange && latitudeY > companyCoordinateY - signRange && latitudeY < companyCoordinateY + signRange)
    // 是否可以打卡
    circularColor.value = !isNotClockTime && !isOutClockTime && isInLocation
    // 是否异常打卡
    abnormal.value = isLate || isEarly
    // 显示备注
    describeRange.value = !isInLocation ? '暂未进入打卡范围 ' : (isNotClockTime ? '未达到打卡时间段,已进入打卡范围 ' : (isOutClockTime ? '超出打卡时间段,已进入打卡范围 ' : '已进入打卡范围'))
    // 显示标题
    if (!circularColor.value)
        signInName.value = '不可打卡'
    else if (abnormal.value)
        signInName.value = isLate ? `${photoText}迟到打卡` : `${photoText}早退打卡`
    else if (isPhoto)
        signInName.value = '拍照打卡'
    else
        signInName.value = isToWork ? `${photoText}上班打卡` : `${photoText}下班打卡`
    // 拍照打卡获取当前位置文字
    if (isPhoto) {
        const location = `${latitude.value},${longitude.value}`
        const locationRes = await amapRegeo({
            location,
        })
        formatAddress.value = locationRes.result.address.length === 0 ? '' : locationRes.result.address
    }
}

/**
 * 初始化提示文案
 * @param res 签到记录对象
 * @returns Promise<void>
 */
async function initTips(res: SignRecordDto) {
    // 支付宝没有微工卡，暂时不做
    // // 0 拍照打卡 1 人脸打卡 人脸打卡模式下需要授权薪工卡
    if (res.signInMode === 1)
        await openSalary()

    // 判断是否需要提现
    toBeWithdraw.value = res.salaryCollectionRecord?.some(i => i.status === 0 || i.status === 3 || i.status === 4)
}

/**
 * 是否开通微工卡
 *
 * @returns 返回一个Promise对象，成功时resolve为true，失败时reject并提示用户未开启微工卡授权
 */
async function openSalary() {
    return new Promise(async (resolve, reject) => {
        await querySalaryCard(detail.value.companyId).catch(() => {
            uni.showModal({
                title: '提示',
                content: '您还未开启微工卡授权，授权后才可打卡核身，请前往微信小程序打卡',
                showCancel: false,
            })
            reject(new Error('未开启微工卡'))
        })
        resolve(true)
    })
}

/**
 * 执行打卡下班
 *
 * @returns 无返回值，异步执行
 */
async function onClockOut() {
    if (detail.value.signInMode === 1) {
        // 人脸打卡模式下需要授权薪工卡
        await openSalary()
        // #ifdef MP-ALIPAY
        uni.showModal({
            title: '提示',
            content: '当前支付宝暂不支持人脸识别功能，需注册微信端“子弹工作”打卡，为您带来不便深感抱歉，我们将尽快处理',
            showCancel: false,
            confirmText: '知道了',
        })
        // #endif
    }
    if (circularColor.value && detail.value.signInMode === 0)
        photoClock()
}

/**
 * 上传照片打卡
 *
 * @returns 无返回值
 */
async function photoClock() {
    const imageRes = await chooseImage({
        count: 1,
        sizeType: ['compressed'],
        sourceType: ['camera'],
    }).catch((err) => {
        console.log(err)
    })
    const tempPath = imageRes?.tempFilePaths?.[0]
    if (!tempPath)
        return
    const res = await uploadFile({
        url: `${globalVariable.baseUrl}/api/v1/blue/blue-sign/upload`,
        filePath: tempPath,
        name: 'file',
        header: { Authorization: (globalVariable.platForm === 'mp-alipay' ? 'Alipay ' : 'Wx ') + globalVariable.token },
        formData: {
            signAddress: formatAddress.value,
        },
    }).catch(() => {
        toast('图片上传失败')
    })

    if (res?.statusCode !== 200)
        return
    const data = JSON.parse(res.data)
    staticData.signVoucher = data.data.fileUrl
    punchIn()
    const event = await navigateTo({
        url: '/package-clock/pages/success/success',
    })
    event.eventChannel.emit('channelData', {
        title: '上传成功',
        content: '已完成打卡',
        signVoucher: staticData.signVoucher,
    })
}

/**
 * 打卡函数
 *
 * @returns 异步操作结果
 */
async function punchIn() {
    await postPunchIn({
        projectId: projectId.value,
        signLocation: `${longitude.value},${latitude.value}`,
        signTime: dayjs().format('HH:mm:ss'),
        date: dayjs().format('YYYY-MM-DD'),
        macAddressList: devices.value,
        signVoucher: staticData.signVoucher,
    }).catch((err) => {
        // #ifdef MP-ALIPAY
        // 上报支付宝埋点
        my.reportCustomEvent('104528073', {
            bizType: 'cloudappevent',
            cur_user_id: user.userInfo.value.userId,
            failmsg: 'service',
            longitude_latitude: '',
            remark: err || '打卡失败',
            status: 0,
        })
        // #endif
    })

    // #ifdef MP-ALIPAY
    // 上报支付宝埋点
    my.reportCustomEvent('104528073', {
        bizType: 'cloudappevent',
        cur_user_id: user.userInfo.value.userId,
        failmsg: 'none',
        longitude_latitude: '',
        remark: '打开成功',
        status: 1,
    })
    // #endif

    initShow(changeDate.value)

    if (!staticData.signVoucher)
        toast('打卡成功', 2000)
}

function onGoWithdraw() {
    navigateTo({
        url: '/package-mine/pages/salary/salary',
    })
}
// 日历点击事件
function onDateChange(date: string) {
    initShow(date)
}

onLoad(async (options) => {
    projectId.value = options?.projectId ? Number(options?.projectId) : -1
    type.value = options?.type ? Number(options?.type) : -1

    todayDate.value = dayjs().format('YYYY-MM-DD')
    // 实时同步显示的打卡时间
    setCurrentTime()
    timer.value = setInterval(() => {
        setCurrentTime()
    }, 1000)

    type.value === 1 && await getAuth()
})

onUnload(() => {
    clearInterval(timer.value)
})
</script>

<!-- <script lang="ts">
export default {
    options: {
        virtualHost: true,
        styleIsolation: 'shared',
    },
}
</script> -->

<template>
    <NavbarContainer title="打卡领薪">
        <view class="box-border px-32rpx text-xs pb-safe-20rpx">
            <Calendar v-if="type === 1" v-model:date="changeDate"
                      :selected="detail.params ? Object.keys(detail.params) : []" @click="onDateChange" />
            <view class="mt-20rpx flex rounded-2xl bg-white p-30rpx">
                <image class="mr-20rpx h-82rpx w-82rpx"
                       :src="`../../../static/images/clock/${type === 1 ? 'long-worker' : 'day-laborer'}.png`" />
                <view class="flex-1">
                    <view class="text-lg text-#1e2038 font-bold">{{ detail.serveCompanyName || '暂无' }}</view>
                    <view v-if="commutingStatus !== 2" class="mt-8rpx flex">
                        <view class="rounded-2xl bg-op-10 px-16rpx py-16rpx"
                              :class="{ 'bg-primary text-primary': commutingStatus === 1, 'bg-orange text-orange': commutingStatus === 0 }">
                            {{ commutingStatus === 1 ? '正常' : '异常' }}
                        </view>
                    </view>
                    <view class="mt-20rpx flex justify-between">
                        <view class="flex">
                            <view class="text-textgray">上班时间：</view>
                            <view>{{ detail.signTimeStart || '暂无' }}</view>
                        </view>
                        <view class="flex">
                            <view class="text-textgray">下班时间：</view>
                            <view>{{ detail.signTimeEnd || '暂无' }}</view>
                        </view>
                    </view>
                    <view class="mt-20rpx">
                        <text class="text-textgray">打卡地点：</text>
                        <text>{{ detail.signPlace || '暂无' }}</text>
                    </view>
                </view>
            </view>
            <view class="ml-20rpx mt-30rpx">
                <view class="mb-55rpx flex items-center text-base">
                    <view
                        class="relative h-16rpx w-16rpx flex-shrink-0 rounded-full bg-textgray after:absolute after:left-1/2 after:top-16rpx after:h-80rpx after:transform after:border-l-1 after:border-l-[rgba(148,151,177,0.1)] after:border-l-dashed after:content-empty after:-translate-x-1/2" />
                    <view class="ml-40rpx text-textgray">上班打卡</view>
                    <view class="mx-20rpx">{{ detail.signTimeIn || '未打卡' }}</view>
                    <view v-if="toWorkStatus !== 2">
                        <view class="rounded-2xl bg-op-10 px-16rpx py-16rpx"
                              :class="{ 'bg-primary text-primary': toWorkStatus === 1, 'bg-orange text-orange': toWorkStatus === 0 }">
                            {{ toWorkStatus === 1 ? '正常' : '迟到' }}
                        </view>
                    </view>
                </view>
                <view class="flex items-center text-base">
                    <view class="relative z-10 h-16rpx w-16rpx flex-shrink-0 rounded-full bg-textgray"
                          :class="{ '!bg-orange': detail.salaryCollectionRecord?.length === 0 }" />
                    <view class="ml-40rpx text-textgray">下班打卡</view>
                    <view class="mx-20rpx">{{ detail.signTimeOut || '未打卡' }}</view>
                    <view v-if="offWorkStatus !== 2">
                        <view class="rounded-2xl bg-op-10 px-16rpx py-16rpx"
                              :class="{ 'bg-primary text-primary': offWorkStatus === 1, 'bg-orange text-orange': offWorkStatus === 0 }">
                            {{ offWorkStatus === 1 ? '正常' : '早退' }}
                        </view>
                    </view>
                </view>
            </view>
            <view v-if="type === 1 && (todayDate === changeDate || nightTime === changeDate)"
                  class="mt-60rpx flex flex-col items-center justify-center">
                <view
                    class="bg h-255rpx w-255rpx flex flex-col items-center justify-center border-20rpx border-[rgba(62,95,245,0.1)] rounded-full border-solid text-white"
                    :class="{ 'clock-abnormal': abnormal, 'clock-fail': !circularColor }" @click="onClockOut">
                    <image v-if="detail.signInMode === 0" class="mb-20rpx h-61rpx w-70rpx"
                           src="../../assets/image/camera.png" />
                    <view class="mb-16rpx text-xl" :class="{ '!text-base': detail.signInMode === 0 }">
                        {{ signInName }}
                    </view>
                    <view class="text-xs" :class="{ '!text-base': detail.signInMode === 0 }">{{ currentTime }}</view>
                </view>
                <view class="mt-30rpx flex items-center">
                    <view class="mr-30rpx">
                        <icon type="success" :color="circularColor ? '#04CD5A' : '#FF7642'" :size="18" />
                    </view>
                    <view class="text-xs text-lightgray">{{ describeRange }}</view>
                </view>
                <view v-if="detail.signInMode === 1" class="mt-10rpx text-xs text-orange"
                      :class="{ '!text-lightgray': circularColor }">
                    {{ detail.signPlace || '暂无' }}
                </view>
            </view>
            <view v-if="toBeWithdraw"
                  class="mt-30rpx h-88rpx rounded-full bg-orange text-center text-lg text-white lh-88rpx"
                  @click="onGoWithdraw">
                <view>提现</view>
            </view>
        </view>
    </NavbarContainer>
</template>

<style scoped>
.bg {
    background: linear-gradient(143deg, #92B0FD 0%, #3E5FF5 100%);
    background-clip: content-box;
}

.clock-abnormal {
    background: #FF7642;
    background-clip: content-box;
    border-color: rgba(255, 118, 66, 0.1);
}

.clock-fail {
    background: #9497B1 !important;
    background-clip: content-box;
    border-color: #e7ebfb !important;
}

:deep(.uni-calendar-item--isDay) {
    background-color: #ebeffe !important;
    color: #3E5FF5 !important;
}

:deep(.uni-calendar-item__weeks-box-circle) {
    background-color: #3E5FF5 !important;
}
</style>

<route lang="json">
{
    "style": {
        "navigationBarTitleText": "打卡领薪"
    }
}
</route>
